﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;

namespace Valentia.Kinection.DataTypes
{
    #region KinectDeviceLockedArgs CLASS
    /// <summary>
    /// Kinect device locked event arguments.
    /// </summary>
    public class KinectDeviceLockedArgs : EventArgs
    {
        #region PROPERTIES
        /// <summary>
        /// Gets the kinect device name.
        /// </summary>
        public String KinectDevice { get; private set; }

        /// <summary>
        /// Gets the true if locked else false if released.
        /// </summary>
        public bool Locked { get; private set; }
        #endregion

        #region CONSTRUCTOR
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="kinectDevice">Kinect device name.</param>
        /// <param name="locked">True if locked else false if released.</param>
        public KinectDeviceLockedArgs(String kinectDevice, bool locked)
        {
            this.KinectDevice = kinectDevice;
            this.Locked = locked;
        }
        #endregion
    }
    #endregion

    #region KinectDeviceStatusChagnedArgs CLASS
    /// <summary>
    /// Kinect device status changed event arguments.
    /// </summary>
    public class KinectDeviceStatusChagnedArgs : EventArgs
    {
        #region PROPERTIES
        /// <summary>
        /// Gets the kinect device name.
        /// </summary>
        public String KinectDevice { get; private set; }

        /// <summary>
        /// Gets the kinect device status.
        /// </summary>
        public KinectDeviceStatus KinectDeviceStatus { get; private set; }
        #endregion

        #region CONSTRUCTOR
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="kinectDevice">Kinect device name.</param>
        /// <param name="kinectDeviceStatus">Kinect device status.</param>
        public KinectDeviceStatusChagnedArgs(String kinectDevice, KinectDeviceStatus kinectDeviceStatus)
        {
            this.KinectDevice = kinectDevice;
            this.KinectDeviceStatus = kinectDeviceStatus;
        }
        #endregion
    }
    #endregion

    #region NotificationReceivedArgs CLASS
    /// <summary>
    /// Notification received event arguments.
    /// </summary>
    [Serializable]
    public class NotificationReceivedArgs : EventArgs
    {
        #region PROPERTIES
        /// <summary>
        /// Gets the notification type.
        /// </summary>
        public NotificationType NotificationType { get; private set; }

        /// <summary>
        /// Gets the notification message.
        /// </summary>
        public String Message { get; private set; }

        /// <summary>
        /// Gets the exception.
        /// </summary>
        public Exception Exception { get; private set; }
        #endregion

        #region CONSTRUCTOR
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="notificationType">Notification type.</param>
        /// <param name="message">Notification message.</param>
        /// <param name="exception">Exception.</param>
        public NotificationReceivedArgs(NotificationType notificationType, String message, Exception exception)
        {
            this.NotificationType = notificationType;
            this.Message = message;
            this.Exception = exception;
        }
        #endregion
    }
    #endregion

    #region ColorFrameReceivedArgs CLASS
    /// <summary>
    /// Color frame received event arguments.
    /// </summary>
    public class ColorFrameReceivedArgs : EventArgs
    {
        #region PROPERTIES
        /// <summary>
        /// Gets the resoloution of image.
        /// </summary>
        public KinectColorImageFormat Resolution { get; private set; }

        /// <summary>
        /// Gets the kinect device name from which this frame is generated.
        /// </summary>
        public String KinectDevice { get; private set; }

        /// <summary>
        /// Gets the bitmap of the image frame.
        /// </summary>
        public Bitmap Bitmap { get; private set; }
        #endregion

        #region CONSTRUCTOR
        /// <summary>
        /// Constructor. For Video image type.
        /// </summary>
        /// <param name="frameType">Frame type.</param>
        /// <param name="resolution">Resoloution of image.</param>
        /// <param name="kinectDevice">Kinect device name from which this frame is generated.</param>
        /// <param name="bitmap">Bitmap of the image frame.</param>
        public ColorFrameReceivedArgs(KinectColorImageFormat resolution, String kinectDevice, Bitmap bitmap)
        {
            this.Resolution = resolution;
            this.KinectDevice = kinectDevice;
            this.Bitmap = bitmap;
        }
        #endregion
    }
    #endregion

    #region DepthFrameReceivedArgs CLASS
    /// <summary>
    /// Depth frame received event arguments.
    /// </summary>
    public class DepthFrameReceivedArgs : EventArgs
    {
        #region PROPERTIES
        /// <summary>
        /// Gets the resoloution of image.
        /// </summary>
        public KinectDepthImageFormat Resolution { get; private set; }

        /// <summary>
        /// Gets the kinect device name from which this frame is generated.
        /// </summary>
        public String KinectDevice { get; private set; }

        /// <summary>
        /// Gets the bitmap of the image frame.
        /// </summary>
        public Bitmap Bitmap { get; private set; }

        /// <summary>
        /// Gets the number of bytes per pixel.
        /// </summary>
        public int BytesPerPixel { get; private set; }

        /// <summary>
        /// Gets the width of image.
        /// </summary>
        public int Width { get; private set; }

        /// <summary>
        /// Gets the height of image.
        /// </summary>
        public int Height { get; private set; }

        /// <summary>
        /// Gets the pixel data if image is depth image.
        /// </summary>
        public short[] DepthPixelData { get; private set; }
        #endregion

        #region CONSTRUCTOR
        /// <summary>
        /// Constructor. For depth image type.
        /// </summary>
        /// <param name="frameType">Frame type.</param>
        /// <param name="resolution">Resoloution of image.</param>
        /// <param name="kinectDevice">Kinect device name from which this frame is generated.</param>
        /// <param name="bitmap">Bitmap of the image frame.</param>
        /// <param name="bytesPerPixel">Number of bytes per pixel.</param>
        /// <param name="width">Width of image.</param>
        /// <param name="height">Height of image.</param>
        /// <param name="depthPixelData">Imaga data bytes.</param>
        public DepthFrameReceivedArgs(
            KinectDepthImageFormat resolution, String kinectDevice, Bitmap bitmap, int bytesPerPixel, int width, int height,
            short[] depthPixelData)
        {
            this.Resolution = resolution;
            this.KinectDevice = kinectDevice;
            this.Bitmap = bitmap;

            this.BytesPerPixel = bytesPerPixel;
            this.Width = width;
            this.Height = height;
            this.DepthPixelData = depthPixelData;
        }
        #endregion
    }
    #endregion

    #region SkeletonFrameReceivedArgs CLASS
    /// <summary>
    /// Skeleton frame received event arguments.
    /// </summary>
    public class SkeletonFrameReceivedArgs : EventArgs
    {
        #region PROPERTIES
        /// <summary>
        /// Gets the kinect device name from which this frame is generated.
        /// </summary>
        public String KinectDevice { get; private set; }

        /// <summary>
        /// Gets the resoloution of color image.
        /// </summary>
        public KinectColorImageFormat ColorImageResoloution { get; private set; }

        /// <summary>
        /// Gets the collection of skeletons.
        /// </summary>
        public SkeletonJointCollection[] Skeletons { get; private set; }
        #endregion

        #region CONSTRUCTOR
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="kinectDevice">Kinect device name from which this frame is generated.</param>
        /// <param name="colorImageResoloution">Color image resoloution.</param>
        /// <param name="skeletons">Collection of skeletons.</param>
        public SkeletonFrameReceivedArgs(String kinectDevice, KinectColorImageFormat colorImageResoloution, SkeletonJointCollection[] skeletons)
        {
            this.KinectDevice = kinectDevice;
            this.ColorImageResoloution = colorImageResoloution;
            this.Skeletons = skeletons;
        }
        #endregion

        #region SERIALIZATION METHODS
        /// <summary>
        /// Converts this object into byte.
        /// </summary>
        /// <returns>Bytes arrray.</returns>
        public byte[] GetBytes()
        {
            using (System.IO.MemoryStream stream = new System.IO.MemoryStream())
            {
                using (System.IO.BinaryWriter writer = new System.IO.BinaryWriter(stream))
                {
                    writer.Write(this.KinectDevice.Length);
                    writer.Write(Encoding.Unicode.GetBytes(this.KinectDevice));
                    writer.Write((int)(this.ColorImageResoloution));
                    writer.Write(this.Skeletons.Length);

                    foreach (SkeletonJointCollection collection in this.Skeletons)
                    {
                        writer.Write(collection.TrackingID);
                        writer.Write((int)(collection.ClippedEdges)); 
                       
                        writer.Write(collection.Count);
                        foreach (SkeletonJointInfo joint in collection)
                        {
                            writer.Write((int)(joint.JointID));
                            writer.Write(joint.ColorX);
                            writer.Write(joint.ColorY);

                            writer.Write(joint.Vector.W);
                            writer.Write(joint.Vector.X);
                            writer.Write(joint.Vector.Y);
                            writer.Write(joint.Vector.Z);

                            writer.Write(joint.JointDepth.DepthX);
                            writer.Write(joint.JointDepth.DepthY);
                            writer.Write(joint.JointDepth.DepthValue);
                            writer.Write(joint.JointDepth.PlayerIndex);
                        }
                    }

                    writer.Flush();
                    stream.Flush();

                    return stream.ToArray();
                }
            }
        }

        /// <summary>
        /// Constructs the class object from bytes buffer.
        /// </summary>
        /// <param name="buffer">Bytes buffer.</param>
        /// <returns>This class object.</returns>
        public static SkeletonFrameReceivedArgs FromBytes(byte[] buffer)
        {
            using (System.IO.MemoryStream stream = new System.IO.MemoryStream(buffer))
            {
                using (System.IO.BinaryReader reader = new System.IO.BinaryReader(stream))
                {
                    String deviceName = Encoding.Unicode.GetString(reader.ReadBytes(reader.ReadInt32() * 2));
                    KinectColorImageFormat imageRes = (KinectColorImageFormat)(reader.ReadInt32());
                    int totalSkeletons = reader.ReadInt32();
                    List<SkeletonJointCollection> skeletons = new List<SkeletonJointCollection>(totalSkeletons);

                    while (totalSkeletons > 0)
                    {
                        totalSkeletons--;

                        int trackingID = reader.ReadInt32();
                        KinectClippedEdges quality = (KinectClippedEdges)(reader.ReadInt32());

                        int totalJoints = reader.ReadInt32();
                        Dictionary<SkeletonJointID, SkeletonJointInfo> joints = new Dictionary<SkeletonJointID, SkeletonJointInfo>(totalJoints);
                        while (totalJoints > 0)
                        {
                            totalJoints--;

                            SkeletonJointID skeletonJointID = (SkeletonJointID)(reader.ReadInt32());
                            int colorX = reader.ReadInt32();
                            int colorY = reader.ReadInt32();

                            int vW = reader.ReadInt32();
                            int vX = reader.ReadInt32();
                            int vY = reader.ReadInt32();
                            int vZ = reader.ReadInt32();

                            int dX = reader.ReadInt32();
                            int dY = reader.ReadInt32();
                            int dV = reader.ReadInt32();
                            int playerIndex = reader.ReadInt32();

                            joints.Add(
                                skeletonJointID,
                                new SkeletonJointInfo(skeletonJointID, colorX, colorY, new SkeletonJointVector(vW, vX, vY, vZ), new SkeletonJointDepth(dX, dY, dV, playerIndex)));
                        }

                        skeletons.Add(new SkeletonJointCollection(trackingID, quality, joints));
                    }

                    return new SkeletonFrameReceivedArgs(deviceName, imageRes, skeletons.ToArray());
                }
            }
        }
        #endregion
    }
    #endregion

    #region NoSkeletonFrameArgs CLASS
    /// <summary>
    /// No skeleton frame event arguments.
    /// </summary>
    public class NoSkeletonFrameArgs : EventArgs
    {
        #region PROPERTIES
        /// <summary>
        /// Gets the kinect device name for which no skeleton data is generated.
        /// </summary>
        public String KinectDevice { get; private set; }
        #endregion

        #region CONSTRUCTOR
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="kinectDevice">Kinect device name for which no skeleton data is generated.</param>
        public NoSkeletonFrameArgs(String kinectDevice)
        {
            this.KinectDevice = kinectDevice;
        }
        #endregion
    }
    #endregion
}
